import collections
import operator
from datetime import datetime
from typing import List, Optional, Any

from bson import ObjectId
from fastapi import APIRouter, Body, Query, Header
from loguru import logger

import controller.activity
import schemas
from api.dataapi.task import task_alumni_belongs_permission, task_activity_institution, task_activity_contacts

from components.router import CenterRoute
from config import col_top11e
from controller.activity_institution import bulk_alter_institutions_with_checking, crud_delete_related_institution, \
    crud_institution_metric_by_activity_scores
from controller.activity_register import bulk_alter_registers_with_checking, crud_validate_label, \
    crud_delete_related_alumni, crud_role_related_alumni, crud_metric_by_activity_type, crud_metric_by_activity_head, \
    crud_metric_by_activity_ranking, crud_metric_by_activity_scores
from ioservice.common.mongo import find_many_docs
from ioservice.const import top11e_col, activity_register_col, institution_top11_col, institution_top11e_col
from models.activity import Activity, ActivityRegister, reasons, ValidateModel, RoleAlumni, ActivityInstitution
from task_utils.task_alumni_rank_score import job_person_score
from task_utils.task_es import job_delete_contact_by_act, job_delete_activity, job_update_activity, \
    job_task_institution_overview, job_task_update_activity_span, job_task_update_dept_to_es, job_task_radar, \
    job_update_alumni_to_es, job_check_orphan_contact_and_del_from_es, workflow_creat_edit_alumni, \
    workflow_cancel_contacts, workflow_creat_edit_activity, workflow_delete_act, workflow_alumni_join_activity
from task_utils.task_jobs import job_update_ranking, job_join_act_contacts, job_institution_to_act, \
    job_update_alumni_completion_score, job_alumni_join_act, job_update_ranking_v2, job_alumni_belong_dept

router = APIRouter(route_class=CenterRoute)


@router.post("/activity/institution/count", description="赋能组织个数")
async def activities_institution(param: schemas.SearchParam = None, permission_ids: Optional[str] = Query(default=None)):
    visibility_ids = permission_ids.split(",") if permission_ids else []
    return await controller.activity.search_institution_count_by_param_with_user(param, visibility_ids)


@router.post("/activity/bulk", description="创建活动")
async def create_activities(activities: List[Activity]):
    logger.info(activities)
    inserted_ids = await controller.activity.bulk_insert_activities_with_checking(activities)
    act_ids = {"ids": [str(i) for i in inserted_ids]}
    # task 部门回显到活动表
    logger.info(f"task::dept cache {[str(i) for i in inserted_ids]}")
    # for mongo
    job_institution_to_act.apply_async(args=[inserted_ids ])

    job_update_ranking.apply_async(countdown=60, expire=120)
    job_update_ranking_v2.apply_async(countdown=45, expire=100)
    workflow_creat_edit_activity.apply_async(args=[inserted_ids])

    for act_id in act_ids['ids']:
        # 更新活动的部门
        task_activity_institution(str(act_id))
    return act_ids


@router.put("/activity/bulk")
async def alter_activities(activities: List[Activity], type: Optional[str] = None):
    act_ids = []
    for a in activities:
        a.fields_check(include=["id"], exclude=["ocode", "ocode_num", "create_time"])
        act_ids.append(str(a.id))
        if type == 'plain':
            del a.importance_of_activities
            del a.duration
            del a.activity_type
            del a.activity_type_two

    modified_num = await controller.activity.bulk_update_activities_with_checking(activities)

    # 更新部门到人身上
    for act_id in act_ids:
        job_alumni_belong_dept(act_id)
        # 更新活动的部门
        task_activity_institution(str(act_id))

    # task 部门回显到活动表
    logger.info(f"task::dept cache {act_ids}")
    workflow_creat_edit_activity.apply_async(args=[act_ids], queue='es')

    job_update_ranking.apply_async(countdown=75, expire=120)
    job_update_ranking_v2.apply_async(countdown=90, expire=100)

    return {"modified_num": modified_num}


@router.delete("/activity/bulk")
async def delete_activities(activities_ids: List[str] = Body(None, embed=True)):
    deleted_num = await controller.activity.bulk_delete_activities(activities_ids)

    # 删除活动 更新排行表
    job_update_ranking.apply_async(countdown=60, expire=120)
    job_update_ranking_v2.apply_async(countdown=45, expire=100)

    job_task_update_dept_to_es.apply_async(countdown=20, expire=90)

    data = await find_many_docs(activity_register_col,

                                query={"activity_id": {"$in": [ObjectId(act) for act in activities_ids]}})
    person_ids = []
    for doc in data:
        person_id = doc.get("person_id")
        person_ids.append(person_id)
        # 权限更新overwrite
        task_alumni_belongs_permission(str(person_id))
        job_person_score(person_id)  # 写入mon+redis

    # es 删除活动+报名的人数
    workflow_delete_act.apply_async(args=[activities_ids, person_ids], queue='es')


    return {"deleted_num": deleted_num}


@router.post("/activity/search", response_model=schemas.SearchResult, response_model_exclude_unset=True,
             description="活动列表")
async def search_activities(param: schemas.SearchParam = None, permission_ids: Optional[str] = Query(default=None)):
    """权限列表"""

    visibility_ids = permission_ids.split(",") if permission_ids else []
    return await controller.activity.search_activity_by_param_with_user(param, visibility_ids)


@router.post("/activity/register", response_model=schemas.AlterModelResult, response_model_exclude_unset=True,
             description="活动关联人才")
async def alter_register(records: List[ActivityRegister]):
    """
    校友参与活动记录编辑或更新
    - 目前用于批量上传与会人员
    - 同一活动的统一人员重新传入直接覆盖之前的与会信息
    :param records:
    :return:
    """
    person_ids = []
    act_ids = []
    for r in records:
        person_ids.append(str(r.person_id))
        act_ids.append(str(r.activity_id))
        r.fields_check(include=['person_id', 'activity_id', 'is_attended'])
    result = await bulk_alter_registers_with_checking(records)

    for act in act_ids:
        task_activity_contacts(act)  # to mongo
        # 计算完成完整度
        job_update_alumni_completion_score.apply_async(args=[person_ids], queue='high')
        workflow_alumni_join_activity.apply_async(args=[act, person_ids], queue='es')

    return result

@router.post("/activity/institution", response_model=schemas.AlterModelResult, response_model_exclude_unset=True,
             description="活动关联赋能组织")
async def alter_institution(records: List[ActivityInstitution]):
    """
    组织参与活动记录编辑或更新
    - 目前用于批量上传组织
    - 同一活动的统一组织重新传入直接覆盖之前的与会信息
    :param records:
    :return:
    """
    institution_ids = []
    act_ids = []
    for r in records:
        institution_ids.append(str(r.institution_id))
        act_ids.append(str(r.activity_id))
        r.fields_check(include=['institution_id', 'activity_id', 'is_attended'])
    result = await bulk_alter_institutions_with_checking(records)

    for act in act_ids:
        task_activity_contacts(act)  # to mongo
    job_update_ranking.apply_async()
    job_update_ranking_v2.apply_async()
    #     # 计算完成完整度
    #     job_update_alumni_completion_score.apply_async(args=[institution_ids], queue='high')
    #     workflow_alumni_join_activity.apply_async(args=[act, institution_ids], queue='es')

    return result


@router.post("/activity/institution_records", response_model=schemas.SearchResult, response_model_exclude_unset=True)
async def list_institution_records(params: schemas.SearchParam):
    return await controller.activity_institution.get_institution_records(params)

@router.post("/activity/register_records", response_model=schemas.SearchResult, response_model_exclude_unset=True)
async def list_register_records(params: schemas.SearchParam):
    return await controller.activity_register.get_register_records(params)

@router.post('/activity/{activity_id}/institution/{institution_id}', description="解除机构关联")
async def remote_alumni(activity_id: str, institution_id: str):
    res = await crud_delete_related_institution(activity_id, institution_id)

    # 取消人才关联 更新活动人次
    logger.info(f'task::job_join_act_contacts {activity_id}')
    task_activity_contacts(activity_id)

    workflow_cancel_contacts.apply_async(args=[activity_id, institution_id], countdown=3, queue='es')
    # 更新排行表

    job_update_ranking.apply_async(countdown=60, expires=120)
    job_update_ranking_v2.apply_async(countdown=45, expires=100)
    job_task_update_dept_to_es.apply_async(queue='es')


    return res

@router.post("/activity/{institution}/with-activities", description="根据关联的机构获得活动列表")
async def get_activity_by_institution(institution, param: schemas.SearchParam):
    activities = await controller.activity.crud_activity_by_institution(institution, param)
    return activities


@router.get("/activity/non_attended/reason")
async def list_non_attended_reasons():
    return reasons




@router.post("/activity/validate", description="活动名称字段验证")
async def validate_activity_label(item: ValidateModel) -> int:
    counts = await crud_validate_label(item)
    return counts


@router.post('/activity/{activity_id}/alumni/{person_id}', description="解除人才关联")
async def remote_alumni(activity_id: str, person_id: str):
    res = await crud_delete_related_alumni(activity_id, person_id)

    # 取消人才关联 更新活动人次
    logger.info(f'task::job_join_act_contacts {activity_id}')
    task_activity_contacts(activity_id)
    job_person_score(person_id)

    workflow_cancel_contacts.apply_async(args=[activity_id, person_id], countdown=3, queue='es')
    # 更新排行表

    job_update_ranking.apply_async(countdown=60, expires=120)
    job_update_ranking_v2.apply_async(countdown=45, expires=100)
    job_task_update_dept_to_es.apply_async(queue='es')


    return res


@router.post("/alumni/{person}/with-activities", description="根据关联的人获得活动列表")
async def get_activity_by_alumni(person, param: schemas.SearchParam):
    activities = await controller.activity.crud_activity_by_alumni(person, param)
    return activities


@router.post("/activity/alumni/role")
async def activity_alumni_role(item: RoleAlumni):
    res = await crud_role_related_alumni(item)
    return res


# <-------------------统计分析的模块------------------->

@router.get("/metric/activity-type", description="活动情况")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                  end_year: Optional[int] = None, only_year=False):
    start_year = start_year or datetime.now().year
    res = await crud_metric_by_activity_type(institution_id, start_year, end_year, only_year)
    return res


@router.get("/metric/activity-head", description="活动人次情况")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                  end_year: Optional[int] = None, only_year=False):
    start_year = start_year or datetime.now().year
    res = await crud_metric_by_activity_head(institution_id, start_year, end_year, only_year)
    return res


@router.get("/metric/ranking", description="活动topN情况")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                  end_year: Optional[int] = None):
    start_year = start_year or datetime.now().year
    res = await crud_metric_by_activity_ranking(institution_id, start_year, end_year)
    return res


@router.get("/metric/scores", description="详细部门榜单")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                  end_year: Optional[int] = None):
    start_year = start_year or datetime.now().year
    end_year = end_year if end_year else start_year
    data = await crud_metric_by_activity_scores(start_year, end_year)

    for index, doc in enumerate(data):
        doc["id"] = doc["_id"]
        del doc["_id"]

        _finish_ratio = doc.get("ration")
        doc['ration'] = round(_finish_ratio * 100, 3)
        _score = round(3.2 * _finish_ratio, 3)

        if _finish_ratio == 1.0:
            doc["score"] = 3.2

        if _score > 4:
            doc["score"] = 4
        elif _score <= 1.5:
            doc["score"] = 1.5

        else:
            doc["score"] = _score

        # 质量指标计算
        # 0-20人	60%	3*60%=1.8分
        # 21-50人	70%	3*70%=2.1分
        # 50-100人	80%	3*80%=2.4分
        # 101-200人	90%	3*90%=2.7分
        # 201以上	100%	3*100%=3分
        total_quality_index_count = doc.get("total_quality_index_count",0)

        if total_quality_index_count <= 20:
            doc["quality_index"] = 1.8
        elif total_quality_index_count <= 50:
            doc["quality_index"] = 2.1
        elif total_quality_index_count <= 100:
            doc["quality_index"] = 2.4
        elif total_quality_index_count <= 200:
            doc["quality_index"] = 2.7
        else:
            doc["quality_index"] = 3

        # 激活指标计算
        # 0-10人	60%	1*60%=0.6分
        # 11-25人	70%	1*70%=0.7分
        # 26-50人	80%	1*80%=0.8分
        # 51-100人	90%	1*90%=0.9分
        # 101以上	100%	1*100%=1分

        if doc.get("total_activate_indicators_count") <= 10:
            doc["activate_indicators"] = 0.6
        elif doc.get("total_activate_indicators_count") <= 25:
            doc["activate_indicators"] = 0.7
        elif doc.get("total_activate_indicators_count") <= 50:
            doc["activate_indicators"] = 0.8
        elif doc.get("total_activate_indicators_count") <= 100:
            doc["activate_indicators"] = 0.9
        else:
            doc["activate_indicators"] = 1

        # 数量指标计算
        doc["quantity_indicators"] = 0

        # total_service 计分公式：总分=（完成数/目标数）*80%*3，最高 4 分（超过按 4 分），最
        # 低 1.5 分（低于按 1.5 分）。
        doc["total_service"] = doc.get("total_participants", 0) + doc.get("total_refs_participants", 0)
        doc["quantity_indicators"] = doc["total_service"] / doc["target"] * 0.8 * 3

        if doc["quantity_indicators"] > 3:
            doc["quantity_indicators"] = 3

        elif doc["quantity_indicators"] <= 1.5:
            doc["quantity_indicators"] = 1.5
        doc["quantity_indicators"] = round(doc["quantity_indicators"], 2)

        # 总分 = 质量指标 + 激活指标 + 数量指标
        doc["total_score"] = doc["quality_index"] + doc["activate_indicators"] + doc["quantity_indicators"]

    # 排名按总分来
    ration_sorted = sorted(data, key=operator.itemgetter('total_score'), reverse=True)
    for index, item in enumerate(ration_sorted):
        item['rank'] = index + 1

    _dept_2 = sorted(ration_sorted, key=operator.itemgetter('id'))
    job_update_ranking.delay()
    job_update_ranking_v2.delay()
    job_task_institution_overview.apply_async(queue='es')
    job_task_update_activity_span.apply_async(queue='es')
    job_task_update_dept_to_es.apply_async(queue='es')
    job_task_radar.apply_async(queue='es')
    return [{"byDept": _dept_2}]


@router.get("/metric/enhance/scores", description="详细部门榜单")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                  end_year: Optional[int] = None):
    start_year = start_year or datetime.now().year
    end_year = end_year if end_year else start_year
    data = await find_many_docs(top11e_col, query={"year": start_year})

    for index, doc in enumerate(data):

        doc["id"] = str(doc["_id"])
        del doc["_id"]
        _finish_ratio = doc.get("ration")
        doc['ration'] = round(_finish_ratio * 100, 3)
        _score = round(3.2 * _finish_ratio, 3)

        if _finish_ratio == 1.0:
            doc["score"] = 3.2

        if _score > 4:
            doc["score"] = 4
        elif _score <= 1.5:
            doc["score"] = 1.5

        else:
            doc["score"] = _score

        # 质量指标计算
        # 0-20人	60%	2*60%=1.2分
        # 21-50人	70%	2*70%=1.4分
        # 50-100人	80%	2*80%=1.6分
        # 101-200人	90%	2*90%=1.8分
        # 201以上	100%	2*100%=2分
        quality_index_count = doc.get("quality_index_count",0)
        if quality_index_count <= 20:
            doc["quality_index"] = 1.2
        elif quality_index_count <= 50:
            doc["quality_index"] = 1.4
        elif quality_index_count <= 100:
            doc["quality_index"] = 1.6
        elif quality_index_count <= 200:
            doc["quality_index"] = 1.8
        else:
            doc["quality_index"] = 2

        # 激活指标计算
        # 0-10人	60%	2*60%=1.2分
        # 11-25人	70%	2*70%=1.4分
        # 26-50人	80%	2*80%=1.6分
        # 51-100人	90%	2*90%=1.8分
        # 101以上	100%	2*100%=2分
        activate_indicators_count = doc.get("activate_indicators_count",0)
        if activate_indicators_count <= 10:
            doc["activate_indicators"] = 1.2
        elif activate_indicators_count <= 25:
            doc["activate_indicators"] = 1.4
        elif activate_indicators_count <= 50:
            doc["activate_indicators"] = 1.6
        elif activate_indicators_count <= 100:
            doc["activate_indicators"] = 1.8
        else:
            doc["activate_indicators"] = 2

        # 数量指标计算
        doc["quantity_indicators"] = 0

        # total_service 计分公式：总分=（完成数/目标数）*80%*3，最高 4 分（超过按 4 分），最
        # 低 1.5 分（低于按 1.5 分）。
        doc["total_service"] = doc.get("total_participants", 0) + doc.get("total_refs_participants", 0)
        doc["quantity_indicators"] = doc["total_service"] / doc["target"] * 0.8 * 3

        if doc["quantity_indicators"] > 3:
            doc["quantity_indicators"] = 3

        elif doc["quantity_indicators"] <= 1.5:
            doc["quantity_indicators"] = 1.5


        # 保留两位小数
        doc["quantity_indicators"] = round(doc["quantity_indicators"], 2)

        # 总分 = 质量指标 + 激活指标 + 数量指标
        doc["total_score"] = doc["quality_index"] + doc["activate_indicators"] + doc["quantity_indicators"]

    ration_sorted = sorted(data, key=operator.itemgetter('total_score'), reverse=True)
    for index, item in enumerate(ration_sorted):
        item['rank'] = index + 1

    _dept_2 = sorted(ration_sorted, key=operator.itemgetter('id'))
    job_update_ranking.delay()
    job_update_ranking_v2.delay()

    return [{"byDept": _dept_2}]


@router.get("/institution/metric/scores", description="赋能组织详细部门榜单")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                    end_year: Optional[int] = None):
    start_year = start_year or datetime.now().year
    end_year = end_year if end_year else start_year
    data = await crud_institution_metric_by_activity_scores(start_year, end_year)

    for index, doc in enumerate(data):
        doc["id"] = doc["_id"]
        del doc["_id"]

        total_quantity_indicators_count = doc.get("total_quantity_indicators_count")

        target = doc.get("target")
        ration = round(total_quantity_indicators_count / target,6)
        doc['ration'] = ration
        # ) 计分公式：总分=（完成数/目标数）*80%*5，最高 6 分（超过按 6 分计），
        # 最低 2.5 分（低于按 2.5 分计）。
        score = round(ration * 0.8 * 5, 3)

        if score > 5:
            doc["quality_index"] = 5
        elif score <= 2.5:
            doc["quality_index"] = 2.5
        else:
            doc["quality_index"] = score
        doc["activate_indicators"] = 2

        doc["score"] = doc["quality_index"] + doc["activate_indicators"]

    ration_sorted = sorted(data, key=operator.itemgetter('score'), reverse=True)
    for index, item in enumerate(ration_sorted):
        item['rank'] = index + 1
    _dept_2 = sorted(ration_sorted, key=operator.itemgetter('id'))

    job_update_ranking.delay()
    job_update_ranking_v2.delay()
    job_task_institution_overview.apply_async(queue='es')
    job_task_update_activity_span.apply_async(queue='es')
    job_task_update_dept_to_es.apply_async(queue='es')
    job_task_radar.apply_async(queue='es')
    return [{"byDept": _dept_2}]


@router.get("/institution/metric/enhance/scores", description="赋能组织详细部门榜单")
async def metric_by_activity_type(institution_id: Optional[str] = None, start_year: Optional[int] = None,
                                    end_year: Optional[int] = None):
    start_year = start_year or datetime.now().year
    end_year = end_year if end_year else start_year
    data = await find_many_docs(institution_top11e_col, query={"year": start_year})

    for index, doc in enumerate(data):
        doc["id"] = str(doc["_id"])
        del doc["_id"]

        total_quantity_indicators_count = doc.get("quantity_indicators_count")

        target = doc.get("target")
        ration = round(total_quantity_indicators_count / target,6)
        doc['ration'] = ration
        # ) 计分公式：总分=（完成数/目标数）*80%*5，最高 6 分（超过按 6 分计），
        # 最低 2.5 分（低于按 2.5 分计）。
        score = round(ration * 0.8 * 5, 3)

        if score > 5:
            doc["quality_index"] = 5
        elif score <= 2.5:
            doc["quality_index"] = 2.5
        else:
            doc["quality_index"] = score
        doc["activate_indicators"] = 2

        doc["score"] = doc["quality_index"] + doc["activate_indicators"]

    ration_sorted = sorted(data, key=operator.itemgetter('score'), reverse=True)
    for index, item in enumerate(ration_sorted):
        item['rank'] = index + 1
    _dept_2 = sorted(ration_sorted, key=operator.itemgetter('id'))

    job_update_ranking.delay()
    job_update_ranking_v2.delay()
    job_task_institution_overview.apply_async(queue='es')
    job_task_update_activity_span.apply_async(queue='es')
    job_task_update_dept_to_es.apply_async(queue='es')
    job_task_radar.apply_async(queue='es')
    return [{"byDept": _dept_2}]


